/*
 * BTDA2Comm.java
 *
 * Created on 11 marzo 2007, 21.51
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package com.bloomingstars.bluepad.comm;

import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;

/**
 *
 * @author vega
 */
public final class BTDA2Comm extends DA2Comm
{   
    /** Creates a new instance of BTDA2Comm */
    public BTDA2Comm()
    {
    }
    
    public int setup(DA2Address addr)
    {
        address = addr;
        
        try
        {
            LocalDevice localDevice = LocalDevice.getLocalDevice();
            DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
            
            DA2DiscoveryListener listener = new DA2DiscoveryListener(this);
            
            System.out.println("Starting device inquiry...");
            discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
            
            // Wait for device discovery to end
            synchronized (this)
            {
                try
                {
                    wait();
                }
                catch (InterruptedException iex)
                {
                    setupStatus = SetupStatus.SETUP_DEVICE_SEARCH_INTERRUPTED;
                    iex.printStackTrace();
                }
            }
            System.out.println("Device inquiry ended...");
            
            Vector remoteDevices = listener.getRemoteDevicesDiscovered();           

            if (remoteDevices.size() > 0)
            {
                RemoteDevice remoteDevice = (RemoteDevice) remoteDevices.elementAt(0);
                
                System.out.println("Starting service search...");
                UUID[] uuids = new UUID[1];
                uuids[0] = new UUID(0x0003); // RFCOMM
                int[] attrSet = { 0, 1, 2, 3, 4, 256 };
                int transId = discoveryAgent.searchServices(attrSet, uuids, remoteDevice, listener);
                
                // Wait for service discovery to end
                synchronized (this)
                {
                    try
                    {
                        wait();
                    }
                    catch (InterruptedException iex)
                    {
                        iex.printStackTrace();
                        setupStatus = SetupStatus.SETUP_SERVICE_SEARCH_INTERRUPTED;
                    }
                }
                System.out.println("Service discovery ended...");
                
                // Service discovery ended. Store first connection URL
                Vector services = listener.getServicesDiscovered();
                 
                if (services.size() > 0)
                {
                    ServiceRecord sr = (ServiceRecord) services.elementAt(0);
                    String url  = sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false );
                    
                    address.setAddress(url);
                    
                    System.out.println("URL: " + url);
                }
                else if (setupStatus == SetupStatus.SETUP_OK)
                {
                    setupStatus = SetupStatus.SETUP_SERVICE_NOT_FOUND;
                }
            }
            else if (setupStatus == SetupStatus.SETUP_OK)
            {
                setupStatus = SetupStatus.SETUP_DEVICE_NOT_FOUND;
            }
        }
        catch (BluetoothStateException e)
        {
            System.err.println("Problems while inquiring for devices");
            e.printStackTrace();
        }
        
        
        return setupStatus;
    }
    
    public int connect()
    {
        int connStatus = ConnectionStatus.CONN_OK;
        
        try
        {
            conn = (StreamConnection) Connector.open(address.getAddress());
        }
        catch (IOException ioex)
        {
            System.err.println("Problems while opening connection");
            ioex.printStackTrace(); 
            return ConnectionStatus.CONN_ERROR;
        }
        
        try
        {
            in = conn.openInputStream();
        }
        catch (IOException ioex)
        {
            System.err.println("Problems while opening the input stream");
            ioex.printStackTrace();
            return ConnectionStatus.CONN_ERROR;
        }

        try
        {
            out = conn.openOutputStream();
        }
        catch (IOException ioex)
        {
            System.err.println("Problems while opening the output stream");
            ioex.printStackTrace();
            return ConnectionStatus.CONN_ERROR;
        }
                
        // Create and start input thread 
        readThread = new DA2Reader();
        readThread.start();
        
        return connStatus;
    }
    
    public int send(int cmd)
    {
        int txStatus = TxStatus.TX_OK;
        
        String commandString = DA2Protocol.getCommandString(cmd);
        
        try
        {
            out.write(commandString.getBytes());
            out.flush();
            lastCommand = cmd;
        }
        catch (IOException ioex)
        {
            System.err.println("Error sending command" + commandString);
            ioex.printStackTrace();
            txStatus = TxStatus.TX_ERROR;
        }
        
        return txStatus;
    }
    
    public void disconnect()
    {
        try
        {
            readThread.setClosing(true);
            
            in.close();
            out.close();
            conn.close();
        }
        catch (IOException ioex)
        {
            System.err.println("Error closing connection");
            ioex.printStackTrace();            
            setupStatus = SetupStatus.SETUP_UNDEFINED;
        }
    }
           
    /** */
    private DA2Reader readThread;
    /** */
    private StreamConnection conn;
    
    class DA2Reader extends Thread
    {
        public void setClosing(boolean flag)
        {
            closingFlag = flag;
        }
        
        public void run()
        {
            try
            {
                System.out.println("Receiver thread started");
                Thread.sleep(250);
                while (true)
                {                
                    if (in != null) // && in.available()>0)
                    {           
                        int b = in.read();
                        if (b == -1)
                        {
                            System.out.println("EOF Reached");
                        }
                        else
                        {                            
                            // the terminator, in which case return String to
                            // all listeners
                            char ch = (char) b;
                            
                            System.out.println("Read: " + ch);
                            
                            // Append read char to current buffer
                            if (ch != '#')
                            {
                                buffer.append(ch);
                            }
                            // If the terminator is read, then return string
                            // to all listeners
                            else if (ch == '#' && buffer.length()>0)
                            {
                                notifyAllDataReceived();
                                                              
                                // Clean buffer
                                if (buffer.length() > 0)
                                {
                                    buffer.delete(0, buffer.length());
                                }
                            }
                            
                            
                        }                                                           
                    }
                    else
                    {
                        System.out.println("Nothing to read");
                    }

                }
            }
            catch (IOException ex)
            {
                // We're closing the connection, OK
                if (closingFlag)
                {
                    System.out.println("Listener thread closing");
                }
                // An error has occurred, log it
                else
                {
                    System.err.println("Error in receiver thread");
                    ex.printStackTrace();
                    notifyAllConnectionLost();
                }
            }
            catch (InterruptedException iex)
            {                
                System.err.println("Receiver thread interrupted");
                iex.printStackTrace();
            }
        }
        
        /**
         * Notifies all listeners data have been received
         **/
        private void notifyAllDataReceived()
        {
            String receivedString = buffer.toString();

            for (int i=0; i<listeners.size(); i++)            
            {
                DA2CommListener listener = (DA2CommListener) listeners.elementAt(i);

                // Notify string received as a response to last command
                // issued
                listener.dataReceived(receivedString, lastCommand);
            }            
        }
        
        private void notifyAllConnectionLost()
        {
            for (int i=0; i<listeners.size(); i++)            
            {
                DA2CommListener listener = (DA2CommListener) listeners.elementAt(i);

                // Notify connection has been lost
                listener.connectionLost();
            }                        
        }
        
        private boolean closingFlag = false;
        StringBuffer buffer = new StringBuffer();
    }
    
}

